﻿#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>


/* 1. 字符分类函数 */
/*
* 分类字符，决定一个字符属于什么类型
* 包含头文件	ctype.c
*/

// 如果参数符合分类体哦阿健就返回真

//iscntrl		任何控制字符
//isspace		空白字符
//isdigit		十进制字符
//isxdigit	十六进制字符
//islower		小写字母
//isupper		大写字母
//isalpha		字母大小写
//isalnum		字母或数字
//ispunct		标点符号
//isgraph		任何图形字符
//isprint		任何可打印字符


//example
int main_1()
{
	int i = 0;
	char str[] = "Test String.\n";
	char c;
	while (str[i])
	{
		c = str[i];
		if (islower(c))
			c -= 32;
		i++;
	}
	return 0;
}

/* 2. 字符传化函数 */
//int tolower(int c); //将参数传进去的⼤写字⺟转⼩写 
//int toupper(int c); //将参数传进去的⼩写字⺟转⼤写



/* 3. strlen 的使用和模拟实现 */
/*
* size_t strlen ( const char * str );
* 的返回值为 size_t，是⽆符号整形
* 参数指向的字符串必须要以 '\0'
*/

//example_1
int main_2()
{
	const char* str1 = "abcdef";
	const char* str2 = "bbb";
	if (strlen(str2) - strlen(str1) > 0)
	{
		printf("str2>str1\n");
	}
	else
	{
		printf("srt1>str2\n");
	}
	return 0;
}
// 返回类型是 size_t 永远大于 0

//example_2
//strlen 模拟实现

int my_strlen(const char* str)
{
	assert(str);
	if (*str == '\0')
		return 0;
	else
		return 1 + my_strlen(str + 1);
}
// 不创建临时变量 使用递归




/* 4. strcpy 的使用和模拟实现 */
/*
*  char* strcpy(char * destination, const char * source );
*  源字符串必须以 '\0' 结束
*  会将源字符串中的 '\0' 拷⻉到⽬标空间
*  ⽬标空间必须⾜够⼤，以确保能存放源字符串
*  ⽬标空间必须可修改  ！！！常量字符串不可以修改
*/

//模拟实现
#include <assert.h>
char* my_strcpy(char* dest, const char* scr)
{
	char* ret = dest;
	assert(dest != NULL);
	assert(scr != NULL);

	while ((*dest++ = *scr++)) //很巧妙 
	{
		;
	}
	return ret;

}


/* 5.strcat的使用和模拟实现 */
/*
* Appends a copy of the source string to the destination string.
* 源字符串必须以 '\0' 结束
* ⽬标字符串中也得有 \0 ，否则没办法知道追加从哪⾥开始
* ⽬标空间必须有⾜够的⼤，能容纳下源字符串的内容
* ⽬标空间必须可修改
*/


// 模拟实现 strcat 函数

char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest != NULL);
	assert(src != NULL);
	while (*dest)
	{
		dest++;
	}
	while ((*dest++ = *src++))
	{
		;
	}
	return ret;
}


/* 7. strcmp的使用和模拟实现 */
/*
* This function starts comparing the first character of each string.
* 第⼀个字符串⼤于第⼆个字符串，则返回⼤于0的数字
* 第⼀个字符串等于第⼆个字符串，则返回0
* 第⼀个字符串⼩于第⼆个字符串，则返回⼩于0的数字
* ⽐较两个字符串中对应位置上字符ASCII码值的⼤⼩
*/

//模拟实现 strcmp
int my_strcmp(const char* str1, const char* str2)
{
	int ret = 0;
	assert(str1 != NULL);
	assert(str2 != NULL);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;

	}
	return *str1 - *str2;
}


/* strncpy 函数的使用 */
/*
* char * strncpy ( char * destination, const char * source, size_t num );
* Copies the first num characters of source to destination.
* 拷⻉num个字符从源字符串到⽬标空间。
* 如果源字符串的⻓度⼩于num，则拷⻉完源字符串之后，在⽬标的后边追加0，直到num个。
*/



/* 8. strncat 函数的使用 */
/*
* char * strncat ( char * destination, const char * source, size_t num );
* Appends the first num characters of source to destination, plus a terminating null-character.
* If the length of the C string in source is less than num, only the content up to the terminating
null-character is copied.
*/

int main_3()
{
	char str1[20];
	char str2[20];
	strcpy(str1, "To be ");
	strcpy(str2, "or not to be");
	strncat(str1, str2, 20);
	printf("%s\n", str1);
	return 0;
}


/* 9. strncmp 函数的使用 */
/*
* int strncmp ( const char * str1, const char * str2, size_t num );
* ⽐较str1和str2的前num个字符，如果相等就继续往后⽐较，最多⽐较num个字⺟
如果提前发现不⼀样，就提前结束
*/


/* 10. strstr 函数的使用和模拟实现 */
/*
* char * strstr ( const char * str1, const char * str2);
* 函数返回字符串str2在字符串str1中第⼀次出现的位置
* 字符串的⽐较匹配不包含 \0 字符，以 \0 作为结束标志
*/

int main_4()
{
	char str[] = "This is a simple string";
	char* pch;
	pch = strstr(str, "simple");
	printf("%s\n", pch);
	strncpy(pch, "sample", 6);
	printf("%s\n", pch);
	printf("%s\n", str);
	return 0;
}


//模拟实现
char* strstr(const char* str1, const char* str2)
{
	char* cp = (char*)str1;
	char* s1, *s2;
	if (!*str2)
		return (char*)str1;
	while (*cp)
	{
		s1 = cp;
		s2 = (char*)str2;
		while (*s1 && *s2 && !(*s1 - *s2))
			s1++, s2++;
		if (!*s2)
			return cp;

		cp++;
	}
	return NULL;
}

//KMP 算法

/* 11. strock 函数的使用 */
/*
*  char * strtok ( char * str, const char * sep);
*  sep参数指向⼀个字符串，定义了⽤作分隔符的字符集合
*  第⼀个参数指定⼀个字符串，它包含了0个或者多个由sep字符串中⼀个或者
多个分隔符分割的标记。
*  strtok函数找到str中的下⼀个标记，并将其⽤ \0 结尾，返回⼀个指向这
个标记的指针。（注：strtok函数会改变被操作的字符串，所以在使⽤strtok
函数切分的字符串⼀般都是临时拷⻉的内容并且可修改。）
*  strtok函数的第⼀个参数不为 NULL ，函数将找到str中第⼀个标记，
strtok函数将保存它在字符串中的位置。
*  strtok函数的第⼀个参数为 NULL ，函数将在同⼀个字符串中被保存的位置
开始，查找下⼀个标记。
*  如果字符串中不存在更多的标记，则返回 NULL 指针
*/


int main_5()
{
	char arr[] = "192.168.6.111";
	char* sep = ".";
	char* str = NULL;
	for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
	{
		printf("%s\n", str);
	}
	return 0;
}
//可以合理推理 strtok 函数中有 静态变量


/* 12. strerror 函数的使用 */
/*
*  char * strerror ( int errnum );
* C语⾔程序启动的时候就会使⽤⼀个全局的变量errno来记录程序的当前错误码，只不过程序启动
的时候errno是0，表⽰没有错误，当我们在使⽤标准库中的函数的时候发⽣了某种错误，就会将对应
的错误码，存放在errno中，⽽⼀个错误码的数字是整数很难理解是什么意思，所以每⼀个错误码都是
有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。

*/

#include <errno.h>

int main_6()
{
	int i = 0;
	for (i = 0; i <= 10; i++) {
		printf("%s\n", strerror(i));
	}
	return 0;
}


//example

int main()
{
	FILE* pFile;
	pFile = fopen("unexist.ent", "r");
	if (pFile == NULL)
		printf("Error opening file unexist.ent: %s\n", strerror(errno));
	//perror("Error opening file unexist.ent");
	//相当于对 strerror 进行了一层封装
	return 0;
}

